<script setup lang="ts">
import { ref, onMounted, computed, watch, nextTick } from 'vue';
import { MessagePlugin, DialogPlugin } from 'tdesign-vue-next';
import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';

/**
 * @description 在线拼图页面入口
 * @author AI
 * @date 2024-06-09
 * @returns void
 */

// 画布尺寸常量
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 400;

// 页面标题
const pageTitle = ref('在线拼图');

// 支持的图片类型常量
const IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
// 单张图片最大5MB
const MAX_IMAGE_SIZE_MB = 5;

// 支持的图片数量范围
const MIN_IMAGE_COUNT = 2;
const MAX_IMAGE_COUNT = 9;
const imageCountOptions = Array.from({ length: MAX_IMAGE_COUNT - MIN_IMAGE_COUNT + 1 }, (_, i) => i + MIN_IMAGE_COUNT);

// 布局配置
interface GridCell {
  x: number; y: number; w: number; h: number;
}
interface LayoutConfig {
  name: string;
  cells: GridCell[];
}
const layoutMap: Record<number, LayoutConfig[]> = {
  2: [
    { name: '左右', cells: [ { x: 0, y: 0, w: 0.5, h: 1 }, { x: 0.5, y: 0, w: 0.5, h: 1 } ] },
    { name: '上下', cells: [ { x: 0, y: 0, w: 1, h: 0.5 }, { x: 0, y: 0.5, w: 1, h: 0.5 } ] },
  ],
  3: [
    { name: '一大两小', cells: [ { x: 0, y: 0, w: 0.5, h: 1 }, { x: 0.5, y: 0, w: 0.5, h: 0.5 }, { x: 0.5, y: 0.5, w: 0.5, h: 0.5 } ] },
    { name: '三竖排', cells: [ { x: 0, y: 0, w: 1/3, h: 1 }, { x: 1/3, y: 0, w: 1/3, h: 1 }, { x: 2/3, y: 0, w: 1/3, h: 1 } ] },
    { name: '三横排', cells: [ { x: 0, y: 0, w: 1, h: 1/3 }, { x: 0, y: 1/3, w: 1, h: 1/3 }, { x: 0, y: 2/3, w: 1, h: 1/3 } ] },
  ],
  4: [
    { name: '四宫格', cells: [ { x: 0, y: 0, w: 0.5, h: 0.5 }, { x: 0.5, y: 0, w: 0.5, h: 0.5 }, { x: 0, y: 0.5, w: 0.5, h: 0.5 }, { x: 0.5, y: 0.5, w: 0.5, h: 0.5 } ] },
    { name: '一大三小', cells: [ { x: 0, y: 0, w: 0.7, h: 1 }, { x: 0.7, y: 0, w: 0.3, h: 1/3 }, { x: 0.7, y: 1/3, w: 0.3, h: 1/3 }, { x: 0.7, y: 2/3, w: 0.3, h: 1/3 } ] },
    { name: 'L型', cells: [ { x: 0, y: 0, w: 0.7, h: 0.7 }, { x: 0.7, y: 0, w: 0.3, h: 0.3 }, { x: 0.7, y: 0.3, w: 0.3, h: 0.4 }, { x: 0, y: 0.7, w: 1, h: 0.3 } ] },
    { name: 'T型', cells: [ { x: 0, y: 0, w: 1, h: 0.4 }, { x: 0, y: 0.4, w: 0.3, h: 0.6 }, { x: 0.3, y: 0.4, w: 0.4, h: 0.6 }, { x: 0.7, y: 0.4, w: 0.3, h: 0.6 } ] },
  ],
  5: [
    { name: '五等分', cells: [ { x: 0, y: 0, w: 0.2, h: 1 }, { x: 0.2, y: 0, w: 0.2, h: 1 }, { x: 0.4, y: 0, w: 0.2, h: 1 }, { x: 0.6, y: 0, w: 0.2, h: 1 }, { x: 0.8, y: 0, w: 0.2, h: 1 } ] },
    { name: '左大右四小', cells: [ { x: 0, y: 0, w: 0.6, h: 1 }, { x: 0.6, y: 0, w: 0.4, h: 0.25 }, { x: 0.6, y: 0.25, w: 0.4, h: 0.25 }, { x: 0.6, y: 0.5, w: 0.4, h: 0.25 }, { x: 0.6, y: 0.75, w: 0.4, h: 0.25 } ] },
    { name: '两大三小', cells: [ { x: 0, y: 0, w: 0.5, h: 0.7 }, { x: 0.5, y: 0, w: 0.5, h: 0.7 }, { x: 0, y: 0.7, w: 1/3, h: 0.3 }, { x: 1/3, y: 0.7, w: 1/3, h: 0.3 }, { x: 2/3, y: 0.7, w: 1/3, h: 0.3 } ] },
    { name: 'T型', cells: [ { x: 0, y: 0, w: 1, h: 0.4 }, { x: 0, y: 0.4, w: 0.2, h: 0.6 }, { x: 0.2, y: 0.4, w: 0.2, h: 0.6 }, { x: 0.4, y: 0.4, w: 0.2, h: 0.6 }, { x: 0.6, y: 0.4, w: 0.4, h: 0.6 } ] },
  ],
  6: [
    { name: '六宫格', cells: [ { x: 0, y: 0, w: 1/3, h: 0.5 }, { x: 1/3, y: 0, w: 1/3, h: 0.5 }, { x: 2/3, y: 0, w: 1/3, h: 0.5 }, { x: 0, y: 0.5, w: 1/3, h: 0.5 }, { x: 1/3, y: 0.5, w: 1/3, h: 0.5 }, { x: 2/3, y: 0.5, w: 1/3, h: 0.5 } ] },
    { name: '左两大右四小', cells: [ { x: 0, y: 0, w: 0.4, h: 0.5 }, { x: 0, y: 0.5, w: 0.4, h: 0.5 }, { x: 0.4, y: 0, w: 0.6, h: 0.25 }, { x: 0.4, y: 0.25, w: 0.6, h: 0.25 }, { x: 0.4, y: 0.5, w: 0.6, h: 0.25 }, { x: 0.4, y: 0.75, w: 0.6, h: 0.25 } ] },
    // 新增：上下三大三小布局
    { name: '上下三大三小', cells: [
      { x: 0, y: 0, w: 1/3, h: 0.6 },
      { x: 1/3, y: 0, w: 1/3, h: 0.6 },
      { x: 2/3, y: 0, w: 1/3, h: 0.6 },
      { x: 0, y: 0.6, w: 1/3, h: 0.4 },
      { x: 1/3, y: 0.6, w: 1/3, h: 0.4 },
      { x: 2/3, y: 0.6, w: 1/3, h: 0.4 }
    ] },
  ],
  7: [
    {
      name: '上2大下5小',
      cells: [
        { x: 0, y: 0, w: 0.5, h: 0.4 },
        { x: 0.5, y: 0, w: 0.5, h: 0.4 },
        { x: 0, y: 0.4, w: 0.2, h: 0.6 },
        { x: 0.2, y: 0.4, w: 0.2, h: 0.6 },
        { x: 0.4, y: 0.4, w: 0.2, h: 0.6 },
        { x: 0.6, y: 0.4, w: 0.2, h: 0.6 },
        { x: 0.8, y: 0.4, w: 0.2, h: 0.6 }
      ]
    },
    {
      name: '左1大右6小',
      cells: [
        { x: 0, y: 0, w: 0.6, h: 1 },
        { x: 0.6, y: 0, w: 0.4, h: 1/6 },
        { x: 0.6, y: 1/6, w: 0.4, h: 1/6 },
        { x: 0.6, y: 2/6, w: 0.4, h: 1/6 },
        { x: 0.6, y: 3/6, w: 0.4, h: 1/6 },
        { x: 0.6, y: 4/6, w: 0.4, h: 1/6 },
        { x: 0.6, y: 5/6, w: 0.4, h: 1/6 }
      ]
    },
    {
      name: '三大四小',
      cells: [
        { x: 0, y: 0, w: 0.5, h: 0.5 },
        { x: 0.5, y: 0, w: 0.5, h: 0.5 },
        { x: 0, y: 0.5, w: 0.33, h: 0.5 },
        { x: 0.33, y: 0.5, w: 0.17, h: 0.5 },
        { x: 0.5, y: 0.5, w: 0.17, h: 0.5 },
        { x: 0.67, y: 0.5, w: 0.17, h: 0.5 },
        { x: 0.84, y: 0.5, w: 0.16, h: 0.5 }
      ]
    }
  ],
  8: [
    // 新布局：两大六小
    {
      name: '两大六小',
      cells: [
        { x: 0, y: 0, w: 0.5, h: 0.5 },      // 大1
        { x: 0.5, y: 0, w: 0.5, h: 0.5 },    // 大2
        { x: 0, y: 0.5, w: 0.25, h: 0.25 },  // 小1
        { x: 0.25, y: 0.5, w: 0.25, h: 0.25 }, // 小2
        { x: 0.5, y: 0.5, w: 0.25, h: 0.25 },  // 小3
        { x: 0.75, y: 0.5, w: 0.25, h: 0.25 }, // 小4
        { x: 0, y: 0.75, w: 0.5, h: 0.25 },    // 小5
        { x: 0.5, y: 0.75, w: 0.5, h: 0.25 }   // 小6
      ]
    },
    {
      name: '左2大右6小',
      cells: [
        { x: 0, y: 0, w: 0.3, h: 0.5 },
        { x: 0, y: 0.5, w: 0.3, h: 0.5 },
        { x: 0.3, y: 0, w: 0.7, h: 1/6 },
        { x: 0.3, y: 1/6, w: 0.7, h: 1/6 },
        { x: 0.3, y: 2/6, w: 0.7, h: 1/6 },
        { x: 0.3, y: 3/6, w: 0.7, h: 1/6 },
        { x: 0.3, y: 4/6, w: 0.7, h: 1/6 },
        { x: 0.3, y: 5/6, w: 0.7, h: 1/6 }
      ]
    },
    {
      name: '三大五小',
      cells: [
        { x: 0, y: 0, w: 0.33, h: 0.5 },
        { x: 0.33, y: 0, w: 0.34, h: 0.5 },
        { x: 0.67, y: 0, w: 0.33, h: 0.5 },
        { x: 0, y: 0.5, w: 0.2, h: 0.5 },
        { x: 0.2, y: 0.5, w: 0.2, h: 0.5 },
        { x: 0.4, y: 0.5, w: 0.2, h: 0.5 },
        { x: 0.6, y: 0.5, w: 0.2, h: 0.5 },
        { x: 0.8, y: 0.5, w: 0.2, h: 0.5 }
      ]
    }
  ],
  9: [
    {
      name: '三大六小',
      cells: [
        { x: 0, y: 0, w: 0.33, h: 0.5 },
        { x: 0.33, y: 0, w: 0.34, h: 0.5 },
        { x: 0.67, y: 0, w: 0.33, h: 0.5 },
        { x: 0, y: 0.5, w: 0.16, h: 0.5 },
        { x: 0.16, y: 0.5, w: 0.16, h: 0.5 },
        { x: 0.32, y: 0.5, w: 0.16, h: 0.5 },
        { x: 0.48, y: 0.5, w: 0.16, h: 0.5 },
        { x: 0.64, y: 0.5, w: 0.18, h: 0.5 },
        { x: 0.82, y: 0.5, w: 0.18, h: 0.5 }
      ]
    },
    {
      name: '左三大右六小',
      cells: [
        { x: 0, y: 0, w: 0.33, h: 0.33 },
        { x: 0, y: 0.33, w: 0.33, h: 0.34 },
        { x: 0, y: 0.67, w: 0.33, h: 0.33 },
        { x: 0.33, y: 0, w: 0.67, h: 1/6 },
        { x: 0.33, y: 1/6, w: 0.67, h: 1/6 },
        { x: 0.33, y: 2/6, w: 0.67, h: 1/6 },
        { x: 0.33, y: 3/6, w: 0.67, h: 1/6 },
        { x: 0.33, y: 4/6, w: 0.67, h: 1/6 },
        { x: 0.33, y: 5/6, w: 0.67, h: 1/6 }
      ]
    },
    {
      name: '九宫格',
      cells: [
        { x: 0, y: 0, w: 1/3, h: 1/3 },
        { x: 1/3, y: 0, w: 1/3, h: 1/3 },
        { x: 2/3, y: 0, w: 1/3, h: 1/3 },
        { x: 0, y: 1/3, w: 1/3, h: 1/3 },
        { x: 1/3, y: 1/3, w: 1/3, h: 1/3 },
        { x: 2/3, y: 1/3, w: 1/3, h: 1/3 },
        { x: 0, y: 2/3, w: 1/3, h: 1/3 },
        { x: 1/3, y: 2/3, w: 1/3, h: 1/3 },
        { x: 2/3, y: 2/3, w: 1/3, h: 1/3 }
      ]
    }
  ],
};

// 当前图片数量
const imageCount = ref(2);
const layoutIndex = ref(0);
const currentLayout = computed(() => layoutMap[imageCount.value]?.[layoutIndex.value] || layoutMap[imageCount.value]?.[0]);

// 监听图片数量变化时重置布局索引为0
watch(imageCount, () => {
  layoutIndex.value = 0;
});

// 格子图片数组，长度与当前布局格子数一致
interface PuzzleImage {
  url: string;
  file: File;
  id: string;
}
const gridImages = ref<(PuzzleImage | null)[]>([]);

// input元素ref数组
const uploadInputRefs = ref<(HTMLInputElement | null)[]>([]);
function setUploadInputRef(el: any, idx: number) {
  if (el && el instanceof HTMLInputElement) uploadInputRefs.value[idx] = el;
}

// 图片偏移（在格子内拖动）
const imageOffsets = ref<{ x: number; y: number }[]>([]);
// 图片缩放（每个格子）
const imageScales = ref<number[]>([]);
// 监听图片数量或布局变化，重置格子图片数组和input refs，并清空上传图片和所有标记
watch([imageCount, currentLayout], ([count, layout]) => {
  const len = layout?.cells.length || 0;
  gridImages.value = Array(len).fill(null);
  uploadInputRefs.value = Array(len).fill(null);
  imageOffsets.value = Array(len).fill({ x: 0, y: 0 });
  imageScales.value = Array(len).fill(1);
  marks.value = [];
});

function onImageScaleChange(val: number, idx: number) {
  imageScales.value[idx] = val;
}

// 拖动状态
const draggingIdx = ref<number | null>(null);
const dragStart = ref<{ x: number; y: number }>({ x: 0, y: 0 });
const dragOrigin = ref<{ x: number; y: number }>({ x: 0, y: 0 });

function onImageMouseDown(e: MouseEvent, idx: number) {
  e.preventDefault();
  draggingIdx.value = idx;
  dragStart.value = { x: e.clientX, y: e.clientY };
  dragOrigin.value = { ...imageOffsets.value[idx] };
  document.addEventListener('mousemove', onImageMouseMove);
  document.addEventListener('mouseup', onImageMouseUp);
}
function onImageMouseMove(e: MouseEvent) {
  if (draggingIdx.value === null) return;
  const idx = draggingIdx.value;
  const scale = imageScales.value[idx] || 1;
  let dx = (e.clientX - dragStart.value.x) / (40 * scale);
  let dy = (e.clientY - dragStart.value.y) / (40 * scale);
  let x = dragOrigin.value.x + dx;
  let y = dragOrigin.value.y + dy;
  x = Math.max(-1, Math.min(1, x));
  y = Math.max(-1, Math.min(1, y));
  imageOffsets.value[idx] = { x, y };
}
function onImageMouseUp() {
  draggingIdx.value = null;
  document.removeEventListener('mousemove', onImageMouseMove);
  document.removeEventListener('mouseup', onImageMouseUp);
}

function onImageWheel(e: WheelEvent, idx: number) {
  e.preventDefault();
  let scale = imageScales.value[idx] || 1;
  scale += e.deltaY < 0 ? 0.05 : -0.05;
  scale = Math.max(1, Math.min(2, scale));
  imageScales.value[idx] = scale;
}

// 布局选择
function selectLayout(idx: number): void {
  layoutIndex.value = idx;
  // 切换布局时清空上传图片
  const len = layoutMap[imageCount.value]?.[idx]?.cells.length || 0;
  gridImages.value = Array(len).fill(null);
  uploadInputRefs.value = Array(len).fill(null);
}

/**
 * 处理格子上传
 */
function onFileChange(e: Event, idx: number): void {
  const files = (e.target as HTMLInputElement).files;
  if (!files || files.length === 0) return;
  const file = files[0];
  if (!IMAGE_TYPES.includes(file.type)) {
    MessagePlugin.error('仅支持JPG/PNG/WEBP格式图片');
    return;
  }
  if (file.size / 1024 / 1024 > MAX_IMAGE_SIZE_MB) {
    MessagePlugin.error('单张图片不能超过5MB');
    return;
  }
  const reader = new FileReader();
  reader.onload = (ev) => {
    gridImages.value[idx] = {
      url: ev.target?.result as string,
      file,
      id: `${file.name}_${file.size}_${Date.now()}`
    };
  };
  reader.readAsDataURL(file);
  (e.target as HTMLInputElement).value = '';
}

/**
 * 触发格子上传
 */
function triggerUpload(idx: number): void {
  uploadInputRefs.value[idx]?.click();
}

/**
 * 删除格子图片
 */
function onRemoveImage(idx: number): void {
  const dialog = DialogPlugin.confirm({
    header: '确认删除',
    body: '确定要删除该图片吗？',
    confirmBtn: '确定',
    cancelBtn: '取消',
    onConfirm: () => {
      gridImages.value[idx] = null;
      MessagePlugin.success('图片已删除');
      dialog.hide();
    },
    onClose: () => dialog.hide(),
    onCancel: () => dialog.hide(),
  });
}

// 导出整体拼图为图片（高清cover算法，带缩放和偏移）
async function exportAsImage() {
  // 校验所有格子都已上传图片
  const layout = currentLayout.value;
  if (!layout) return;
  const notFilled = layout.cells.some((_, i) => !gridImages.value[i]);
  if (notFilled) {
    let dialog: any = null;
    dialog = DialogPlugin.confirm({
      header: '导出提示',
      body: '请先为所有格子上传图片，未上传图片时无法导出。',
      confirmBtn: '知道了',
      cancelBtn: '取消',
      onConfirm: () => { dialog && dialog.hide(); },
      onCancel: () => { dialog && dialog.hide(); },
    });
    return;
  }
  await nextTick();
  const scale = 2; // 高清导出
  const canvas = document.createElement('canvas');
  canvas.width = CANVAS_WIDTH * scale;
  canvas.height = CANVAS_HEIGHT * scale;
  const ctx = canvas.getContext('2d');
  if (!ctx) return;
  ctx.fillStyle = '#fff';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  for (let i = 0; i < layout.cells.length; i++) {
    const cell = layout.cells[i];
    const imgData = gridImages.value[i];
    if (!imgData) continue;
    const img = new window.Image();
    img.src = imgData.url;
    await new Promise((resolve) => {
      img.onload = () => {
        // cover算法+缩放+偏移
        const cellW = cell.w * CANVAS_WIDTH * scale;
        const cellH = cell.h * CANVAS_HEIGHT * scale;
        const cellX = cell.x * CANVAS_WIDTH * scale;
        const cellY = cell.y * CANVAS_HEIGHT * scale;
        const imgRatio = img.width / img.height;
        const cellRatio = cellW / cellH;
        let drawW, drawH, sx, sy;
        let userScale = imageScales.value[i] || 1;
        if (imgRatio > cellRatio) {
          // 图片比格子宽，左右裁剪
          drawH = img.height / userScale;
          drawW = drawH * cellRatio;
          sx = (img.width - drawW) / 2;
          sy = (img.height - drawH) / 2;
        } else {
          // 图片比格子高，上下裁剪
          drawW = img.width / userScale;
          drawH = drawW / cellRatio;
          sx = (img.width - drawW) / 2;
          sy = (img.height - drawH) / 2;
        }
        // 偏移量（用户拖动）
        const offset = imageOffsets.value[i] || { x: 0, y: 0 };
        sx += offset.x * (drawW / 2);
        sy += offset.y * (drawH / 2);
        // 限制偏移不超出图片边界
        sx = Math.max(0, Math.min(img.width - drawW, sx));
        sy = Math.max(0, Math.min(img.height - drawH, sy));
        ctx.save();
        ctx.beginPath();
        ctx.rect(cellX, cellY, cellW, cellH);
        ctx.clip();
        ctx.drawImage(
          img,
          sx, sy, drawW, drawH,
          cellX, cellY, cellW, cellH
        );
        ctx.restore();
        resolve(true);
      };
    });
  }
  const url = canvas.toDataURL('image/png');
  const a = document.createElement('a');
  a.href = url;
  a.download = '拼图.png';
  a.click();
  MessagePlugin.success('导出成功');
}

// 标记数据结构，去掉arrow类型
const marks = ref<Array<{ type: 'text'|'rect'|'ellipse', x: number, y: number, w: number, h: number, content?: string, id: string, color?: string, editing?: boolean }>>([]);
let markId = 0;
function addMark(type: 'text'|'rect'|'ellipse') {
  marks.value.push({
    type,
    x: 100 + markId * 10,
    y: 100 + markId * 10,
    w: 100,
    h: 40,
    content: type === 'text' ? '双击编辑文字' : undefined,
    id: 'mark_' + (markId++),
    color: '#0052d9',
    editing: false
  });
}
function removeMark(id: string) {
  marks.value = marks.value.filter(m => m.id !== id);
}
// 拖动标记
let draggingMarkId: string | null = null;
let dragMarkOffset = { x: 0, y: 0 };
function onMarkMouseDown(e: MouseEvent, mark: any) {
  if ((e.target as HTMLElement).classList.contains('puzzle-mark-resize')) return;
  e.stopPropagation();
  draggingMarkId = mark.id;
  dragMarkOffset = { x: e.clientX - mark.x, y: e.clientY - mark.y };
  document.addEventListener('mousemove', onMarkMouseMove);
  document.addEventListener('mouseup', onMarkMouseUp);
}
function onMarkMouseMove(e: MouseEvent) {
  if (!draggingMarkId) return;
  const mark = marks.value.find(m => m.id === draggingMarkId);
  if (!mark) return;
  mark.x = e.clientX - dragMarkOffset.x;
  mark.y = e.clientY - dragMarkOffset.y;
}
function onMarkMouseUp() {
  draggingMarkId = null;
  document.removeEventListener('mousemove', onMarkMouseMove);
  document.removeEventListener('mouseup', onMarkMouseUp);
}
// 缩放标记
let resizingMarkId: string | null = null;
let resizeOrigin = { w: 0, h: 0, x: 0, y: 0 };
function onMarkResizeDown(e: MouseEvent, mark: any) {
  e.stopPropagation();
  resizingMarkId = mark.id;
  resizeOrigin = { w: mark.w, h: mark.h, x: e.clientX, y: e.clientY };
  document.addEventListener('mousemove', onMarkResizeMove);
  document.addEventListener('mouseup', onMarkResizeUp);
}
function onMarkResizeMove(e: MouseEvent) {
  if (!resizingMarkId) return;
  const mark = marks.value.find(m => m.id === resizingMarkId);
  if (!mark) return;
  mark.w = Math.max(30, resizeOrigin.w + (e.clientX - resizeOrigin.x));
  mark.h = Math.max(20, resizeOrigin.h + (e.clientY - resizeOrigin.y));
}
function onMarkResizeUp() {
  resizingMarkId = null;
  document.removeEventListener('mousemove', onMarkResizeMove);
  document.removeEventListener('mouseup', onMarkResizeUp);
}
// 文字内联编辑
function onMarkDblClick(mark: any) {
  if (mark.type === 'text') {
    mark.editing = true;
    setTimeout(() => {
      const input = document.getElementById('mark-input-' + mark.id) as HTMLInputElement;
      if (input) input.focus();
    }, 0);
  }
}
function onMarkInputBlur(mark: any, e: any) {
  mark.content = e.target.value;
  mark.editing = false;
}
function onMarkInputEnter(mark: any, e: any) {
  if (e.key === 'Enter') {
    mark.content = e.target.value;
    mark.editing = false;
  }
}
// 改变颜色
function onMarkColorChange(val: string, mark: any) {
  mark.color = val;
}

onMounted(() => {
  const len = currentLayout.value?.cells.length || 0;
  uploadInputRefs.value = Array(len).fill(null);
  gridImages.value = Array(len).fill(null);
  console.log('【在线拼图】页面已加载');
});
</script>

<template>
  <div class="puzzle-page-container">
    <!-- 页面标题 -->
    <h2 class="puzzle-title">{{ pageTitle }}</h2>
    <!-- 功能描述 -->
    <p class="puzzle-desc">支持多图片数量、多布局选择，格子严格跟随布局，支持导出整体拼图。</p>

    <!-- 图片数量选择 -->
    <div class="puzzle-count-select">
      <span>图片数量：</span>
      <t-radio-group v-model="imageCount">
        <t-radio-button v-for="num in imageCountOptions" :key="num" :value="num">{{ num }}张</t-radio-button>
      </t-radio-group>
      <t-button size="small" style="margin-left:16px;" @click="exportAsImage">导出拼图</t-button>
    </div>

    <!-- 布局选择区 -->
    <div class="puzzle-layout-select-list">
      <div
        v-for="(layout, idx) in layoutMap[imageCount]"
        :key="layout.name + idx"
        class="puzzle-layout-thumb"
        :class="{ selected: layoutIndex === idx }"
        @click="selectLayout(idx)"
      >
        <div class="puzzle-layout-thumb-inner">
          <div
            v-for="(cell, cidx) in layout.cells"
            :key="cidx"
            class="puzzle-layout-thumb-cell"
            :style="{
              left: (cell.x * 60) + 'px',
              top: (cell.y * 40) + 'px',
              width: (cell.w * 60) + 'px',
              height: (cell.h * 40) + 'px',
            }"
          ></div>
        </div>
      </div>
    </div>

    <!-- 操作区美化 -->
    <!-- 已移除所有操作按钮 -->

    <!-- 拼图区 -->
    <div class="puzzle-grid-area puzzle-canvas-area">
      <!-- 标记渲染 -->
      <template v-for="mark in marks" :key="mark.id">
        <div
          v-if="mark.type==='text'"
          class="puzzle-mark-text"
          :style="{ left: mark.x+'px', top: mark.y+'px', width: mark.w+'px', height: mark.h+'px', borderColor: mark.color }"
          @mousedown="(e) => onMarkMouseDown(e, mark)"
          @dblclick="() => onMarkDblClick(mark)"
        >
          <input
            v-if="mark.editing"
            :id="'mark-input-' + mark.id"
            v-model="mark.content"
            @blur="(e) => onMarkInputBlur(mark, e)"
            @keydown="(e) => onMarkInputEnter(mark, e)"
            class="puzzle-mark-input"
          />
          <span v-else>{{ mark.content }}</span>
          <span class="puzzle-mark-remove" @click.stop="() => removeMark(mark.id)">×</span>
          <input type="color" class="puzzle-mark-color" v-model="mark.color" @change="(e) => onMarkColorChange((e.target as HTMLInputElement).value, mark)" />
          <span class="puzzle-mark-resize" @mousedown="(e) => onMarkResizeDown(e, mark)"></span>
        </div>
        <div
          v-else-if="mark.type==='rect'"
          class="puzzle-mark-rect"
          :style="{ left: mark.x+'px', top: mark.y+'px', width: mark.w+'px', height: mark.h+'px', borderColor: mark.color }"
          @mousedown="(e) => onMarkMouseDown(e, mark)"
        >
          <span class="puzzle-mark-remove" @click.stop="() => removeMark(mark.id)">×</span>
          <input type="color" class="puzzle-mark-color" v-model="mark.color" @change="(e) => onMarkColorChange((e.target as HTMLInputElement).value, mark)" />
          <span class="puzzle-mark-resize" @mousedown="(e) => onMarkResizeDown(e, mark)"></span>
        </div>
        <div
          v-else-if="mark.type==='ellipse'"
          class="puzzle-mark-ellipse"
          :style="{ left: mark.x+'px', top: mark.y+'px', width: mark.w+'px', height: mark.h+'px', borderColor: mark.color }"
          @mousedown="(e) => onMarkMouseDown(e, mark)"
        >
          <span class="puzzle-mark-remove" @click.stop="() => removeMark(mark.id)">×</span>
          <input type="color" class="puzzle-mark-color" v-model="mark.color" @change="(e) => onMarkColorChange((e.target as HTMLInputElement).value, mark)" />
          <span class="puzzle-mark-resize" @mousedown="(e) => onMarkResizeDown(e, mark)"></span>
        </div>
      </template>
      <div
        v-for="(cell, idx) in currentLayout.cells"
        :key="idx"
        class="puzzle-grid-item"
        :style="{
          left: (cell.x * CANVAS_WIDTH) + 'px',
          top: (cell.y * CANVAS_HEIGHT) + 'px',
          width: (cell.w * CANVAS_WIDTH) + 'px',
          height: (cell.h * CANVAS_HEIGHT) + 'px',
        }"
      >
        <input
          :ref="el => setUploadInputRef(el, idx)"
          type="file"
          accept="image/jpeg,image/png,image/webp"
          style="display: none"
          @change="(e) => onFileChange(e, idx)"
        />
        <template v-if="gridImages[idx]">
          <img
            :src="gridImages[idx]?.url"
            alt="预览"
            class="puzzle-grid-img"
            @mousedown="(e) => onImageMouseDown(e, idx)"
            @wheel="(e) => onImageWheel(e, idx)"
            :style="{
              objectFit: 'cover',
              cursor: 'grab',
              transform: `scale(${imageScales[idx] || 1}) translate(${imageOffsets[idx]?.x * 40 || 0}px, ${imageOffsets[idx]?.y * 40 || 0}px)`
            }"
          />
          <t-button
            theme="danger"
            shape="round"
            size="small"
            class="puzzle-image-remove-btn"
            @click.stop="() => onRemoveImage(idx)"
          >删除</t-button>
        </template>
        <template v-else>
          <div class="puzzle-grid-plus" @click="() => triggerUpload(idx)">+</div>
        </template>
      </div>
      <!-- 右侧空白区域提示已移除 -->
    </div>
  </div>
</template>

<style scoped lang="less">
.puzzle-page-container {
  width: 100%;
  min-height: 100%;
  background: var(--td-bg-color-container, #fff);
  padding: 32px 24px;
  box-sizing: border-box;
}
.puzzle-title {
  font-size: 22px;
  font-weight: 600;
  color: #222;
  margin-bottom: 12px;
}
.puzzle-desc {
  font-size: 15px;
  color: #888;
  margin-bottom: 32px;
}
.puzzle-count-select {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}
.puzzle-layout-select-list {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 24px;
}
.puzzle-layout-thumb {
  width: 80px;
  height: 60px;
  border: 2px solid #e5e6eb;
  border-radius: 8px;
  background: #f7f8fa;
  cursor: pointer;
  position: relative;
  transition: border-color 0.2s;
}
.puzzle-layout-thumb.selected {
  border-color: #0052d9;
}
.puzzle-layout-thumb-inner {
  position: absolute;
  left: 10px;
  top: 10px;
  width: 60px;
  height: 40px;
  background: #fff;
  border-radius: 4px;
}
.puzzle-layout-thumb-cell {
  position: absolute;
  background: #b5d3fa;
  border-radius: 2px;
}
.puzzle-grid-area.puzzle-canvas-area {
  position: relative;
  width: 600px;
  height: 400px;
  background: #f7f8fa;
  border-radius: 16px;
  margin-bottom: 32px;
  box-shadow: 0 2px 8px #e5e6eb44;
}
.puzzle-grid-item {
  position: absolute;
  border: 2px dashed #e5e6eb;
  border-radius: 12px;
  background: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s, box-shadow 0.2s;
  overflow: hidden;
}
.puzzle-grid-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 10px 10px 0 0;
}
.puzzle-image-remove-btn {
  margin-top: 6px;
  width: 60px;
  font-weight: bold;
  background: #fff;
  border: 1px solid #ff4d4f;
  color: #ff4d4f;
  transition: background 0.2s, color 0.2s;
  &:hover {
    background: #ff4d4f;
    color: #fff;
  }
}
.puzzle-grid-plus {
  font-size: 36px;
  color: #8c9bab;
  user-select: none;
  text-align: center;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.puzzle-grid-img[dragging] {
  opacity: 0.7;
  border: 2px dashed #0052d9;
}
.puzzle-ops-bar {
  display: flex;
  gap: 16px;
  margin-bottom: 20px;
  align-items: center;
}
.puzzle-ops-bar .t-button {
  font-weight: bold;
  font-size: 15px;
  border-radius: 20px;
  min-width: 90px;
}
.puzzle-mark-text {
  position: absolute;
  background: #fffbe6;
  color: #222;
  border: 1px solid #ffe58f;
  border-radius: 6px;
  padding: 4px 12px;
  font-size: 16px;
  z-index: 20;
  cursor: move;
  user-select: none;
  display: flex;
  align-items: center;
}
.puzzle-mark-input {
  border: none;
  background: #fffbe6;
  font-size: 16px;
  outline: none;
  width: 80px;
}
.puzzle-mark-remove {
  color: #ff4d4f;
  font-weight: bold;
  margin-left: 8px;
  cursor: pointer;
}
.puzzle-mark-color {
  margin-left: 8px;
  width: 24px;
  height: 24px;
  border: none;
  background: none;
  cursor: pointer;
}
.puzzle-mark-rect {
  position: absolute;
  border: 2px solid #0052d9;
  border-radius: 4px;
  background: transparent;
  z-index: 20;
  cursor: move;
}
.puzzle-mark-ellipse {
  position: absolute;
  border: 2px solid #0052d9;
  border-radius: 50%;
  background: transparent;
  z-index: 20;
  cursor: move;
}
.puzzle-mark-resize {
  position: absolute;
  right: -8px;
  bottom: -8px;
  width: 16px;
  height: 16px;
  background: #fff;
  border: 2px solid #0052d9;
  border-radius: 50%;
  cursor: nwse-resize;
  z-index: 30;
}
.puzzle-mark-arrow {
  position: absolute;
  z-index: 20;
  cursor: move;
  width: 100px;
  height: 40px;
}
.puzzle-mark-arrow-drag {
  position: absolute;
  left: 0; top: 0; right: 0; bottom: 0;
  z-index: 21;
  cursor: move;
}
</style> 